﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Babel.SDK
{
    internal class StackFrameString
    {
        #region Fields
        // Match group of ()
        private static readonly Regex _reSign = new Regex(
                @"^(.*\s+at\s*)?(?<frame>(?<type>.*)\.(?<name>[^(]*(?=\())[^()]*" +
                    @"(?:" +
                    @"(?:(?'open'\()[^()]*)+" +
                    @"(?:(?'close-open'\))[^()]*)+" +
                    @")*" +
                @"(?(open)(?!)))"
                , RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace);

        private static Regex _reArgLs = new Regex(
                @"(?>" +
                    @"(?<arg>(?<name>[^<,]+)" +
                    @"(?<gen>" +
                    @"(?:" +
                    @"  (?:(?'open'<)[^<]*)+" +
                    @"  (?:(?'close-open'>)[^<,]*)+" +
                    @")*" +
                    @"(?(open)(?!))" +
                    @"))\s*,?\s*" +
                @")*",
                RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace);

        private string _frame;
        private string _fullTypeName;
        private string _methodName;
        private List<string> _arguments;
        #endregion

        #region Properties
        public string Frame
        {
            get
            {
                return _frame;
            }
        }

        public string FullTypeName
        {
            get { return _fullTypeName; }
            set { _fullTypeName = value; }
        }

        public string MethodName
        {
            get { return _methodName; }
            set { _methodName = value; }
        }

        public List<string> Arguments
        {
            get { return _arguments; }
        }
        
        #endregion

        #region Constructors
        private StackFrameString(string fullTypeName, string methodName, List<string> arguments)
        {
            if (String.IsNullOrEmpty(methodName))
                throw new ArgumentException("methodName");

            if (arguments == null)
                throw new ArgumentNullException("arguments");

            _fullTypeName = fullTypeName;
            _methodName = methodName;
            _arguments = arguments;
        }
        
        #endregion

        #region Methods
        public static bool TryParse(string text, out StackFrameString obj)
        {
            if (text == null)
                throw new ArgumentNullException("text");

            obj = null;
            if (text.Length == 0)
                return false;

            Match ms = _reSign.Match(text);
            if (ms.Success)
            {
                // Find arguments
                List<string> args;
                if (TrySplitArgumentList(ms.Groups["close"].Value, out args))
                {
                    obj = new StackFrameString(ms.Groups["type"].Value, ms.Groups["name"].Value, args);
                    obj._frame = ms.Groups["frame"].Value;
                    return true;
                }
            }
            return false;
        }

        private static bool TrySplitArgumentList(string text, out List<string> args)
        {
            args = new List<string>();
            Match m = _reArgLs.Match(text);
            if (m.Success)
            {
                foreach (Capture c in m.Groups["arg"].Captures)
                {
                    args.Add(c.Value);
                }
                return true;
            }
            return false;
        }
        
        #endregion
    }
}
